using System;
using System.Linq;

namespace Thunder
{
	public static class UInt32Extensions
	{
		public static byte GetDigitCount(this UInt32 num)
		{
			if (num > 999999999)
				return 10;
			if (num > 99999999)
				return 9;
			if (num > 9999999)
				return 8;
			if (num > 999999)
				return 7;
			if (num > 99999)
				return 6;
			if (num > 9999)
				return 5;
			if (num > 999)
				return 4;
			if (num > 99)
				return 3;
			if (num > 9)
				return 2;
			return 1;
		}
		
		public static byte[] GetDigits(this UInt32 num)
		{
			return num.GetDigits(false);	
		}
		
		public static byte[] GetDigits(this UInt32 num, bool multTwo)
		{
			byte digitCount = num.GetDigitCount();
			byte[] digits = new byte[(multTwo && digitCount % 2 == 1) ? digitCount + 1 : digitCount];
			
			UInt32 multTen = (UInt32)Math.Pow(10, digitCount - 1);
			for (int i = (digits.Length != digitCount) ? 1 : 0; i < digits.Length; ++i)
			{
				digits[i] = (byte)(num / multTen);
				num -= multTen * digits[i];
				multTen /= 10;
			}
			
			return digits;
		}
		
		public static byte[] ToBCDCode(this UInt32 num)
		{			
			byte[] digits = num.GetDigits(true);			
			for (int i = 0; i < digits.Length; ++i)
				if (digits[i] > 9)
					throw new ArgumentOutOfRangeException("Digit value for BCD code needs to be in range 0-9");
			
			byte byteCount = (byte)Math.Ceiling(digits.Length / 2d);
			byte[] bytes = new byte[byteCount];
			
			for (int i = 0, k = 0; i < digits.Length; i += 2, ++k)
				bytes[k] = (byte)(((0xF & digits[i]) << 4) | (0xF & digits[i + 1]));
			
			return bytes;
		}
		
		public static string ToBCDCodeString(this UInt32 num)
		{
			byte[] bcd = num.ToBCDCode();
			
			return string.Join(string.Empty, bcd.Select<byte, string>(b => b.ToString("X2")).ToArray());
		}
		
		public static bool IsEven(this UInt32 num)
		{
			return num % 2 == 0;	
		}
		
		public static bool IsOdd(this UInt32 num)
		{
			return num % 2 != 0;	
		}
		
		public static bool IsInRange(this UInt32 num, UInt32 min, UInt32 max)
		{
			return (num <= max && num >= min);	
		}
		
		public static UInt32 PutInRange(this UInt32 num, UInt32 min, UInt32 max)
		{
			return ((num < min) ? min : (num > max) ? max : num);	
		}
	}
}

